手机彩票 内容
一）
1、代理商机制
国家数据库 -->  代理商  --> 1.子代理商 (黑马彩票公司) --->  用户
代理商  -->2.自己的用户

2、资金流
用户群 ---> 子代理商
用户信息表 用户账户表(充值)  冻结资金表 
投注 ---> 校验数据 -->创建订单表  --->轮询 未处理订单表 打包 发送到代理商 
用户查询 返回数据


子代理商  ---> 代理商

预存资金  ---> 资金预警机制

代理商  ---> 国家出票中心
告知投注结果  处理冻结资金

彩票业务利益链
国家允许损耗  用户2块  子代理商2*8%  代理商 2*4%  数据中心  2*3%  层层盘剥
和网盟一个性质   可见渠道的重要性
一层一层的盘剥相关利益
中间商 		

3、购彩流程

	//其实就是购物车的流程 

	第一步：应用自动获取当前销售期信息  //获取数据
	第二步： 判断用户登陆情况，获取账户信息（可以在第二步之前进行）//判断登陆及获取账户信息 
	第三步：用户选择彩种  //用户操作
	第四步：按照彩种的玩法规则选定号码 
	第五步：核实用户账户余额是否满足投注需求  //判断余额
	第六步：下投注订单到服务器，等待服务器回复  //发送请求 获取返回数据
	第七步：接收到服务器提示信息，若成功将扣除用户账户对应金额  //获取数据 本地操作及更新

	4、项目架构划分
	其实就是应用架构和准备工具
	常用jar包   
	commons-long3.jar...
	大小写转换  排序 长度计算	替换	分割  合并 空字符  截取  屏幕测量  activity管理...
	应用架构：
	网络通信
			《手机客户端接入接口文档1.2》协议封装（xml）//所为的协议 就是 要如何发送请求和处理返回数据
			通过HttpClient实现与服务器通信
			业务操作
			公共信息
			用户信息
			业务层调用(如何降低耦合)
			界面显示
			统一界面管理
			
			
			5、协议的发送--->(这个协议就是请求,把这个理解了就好办多了)
//实际上就是教你处理返回的data  就是做成JavaBeans   Leaf Header  Body  Message Element  ...

//还有数据的处理  时间的格式化  字符串的拼接和剪切  加密信息... 

	//无非就是处理 请求和返回数据  
	//请求是发送一个xml文件 所以才会需要用对象序列化成xml
	//但目前的开发都是发送String 所以不必太在意
			收集用户信息  :
			android or ios  
			电话号  通讯录  短信  
			imei:设备编号   imsi: sim卡编号
			appList : app列表  分析app流行度  用于作为合作商
			6、协议的处理——问题分析

			不同的业务逻辑 请求的参数也不同 如 登陆 注册 查询 投注 参数都不相同
			但也有相同的地方  这时候需要封装和继承

			7、协议的封装
			8、问题处理
			为空  换行符等问题

			9、Element抽象化

			二）
			1、协议通用化
			状态码:可以节省传输errmessage的汉字长度

			通常用json(小) xml(可压缩) 
			wbxml  维护对应关系的可解析的xml  json中的:"" [],也是很耗数据的
			数据压缩		
			但高并发的大数据 只传输value,不要key 
			使用:
			在头前加8个字节,是这个数据包的总长度  
			然后直接截取这个数据包 
			再根据个字段长度截取对应长度的value
			
			
			2、网络链接类型及渠道
			通过HttpClient实现与服务器通信
			实现方式：
			使用Socket通信（豌豆荚（USB、WIFI）、聊天工具）
		使用HttpURLConnection  //快
		使用HttpClient  //稳定
		通信渠道
		WLAN（wi-fi）
		手机APN接入点（基站）
		Wap方式（地域性、增值业务——IP是10.0.0.172（200） 端口是80 ）
		Net方式
		特别注意：
		1、连网时网络类型的判断，当为手机上网时注意代理信息获取。
		2、当使用手机上网时我们的联网代码的编写。

		3、通信工具类编写
//这两个类还需要再研究

		HttpClientUtil.java  
	post get 请求 发送xml StringEntity entity = new StringEntity(xml, "utf-8");  //有汉字 容易乱码
	public InputStream sendXml(String uri, String xml) {}   //post 发送xml的字符串
	client = new DefaultHttpClient();  //初始化client  构造中
	NetUtil.java   判断 wap还是wifi  3/4G 及无网络 boolean isMOBILEConnection  boolean isWIFIConnection
	wap 需要设置代理  
	if (isMOBILE) {
			// APN被选中,的代理信息是否有内容，如果有wap方式
			readAPN(context);// 判断是哪个APN被选中了
		}

		4、Socket在手机上的应用
		usb  wifi  蓝牙 传输 
		usb 手机助手获取pid vid 获取唯一确定设备  让电脑安装驱动 socket发送数据
		wifi pc在局域网内发送udp数据包  手机助手监听接受后 传输数据
		蓝牙  配对  bluetoothSocket
		
		数据量大,实时传输要求高
		聊天工具）
		股票交易
		贵金属交易
		socket 长连接    发送完  不关闭连接
		手机端 默认1分钟回收socket  
		解决  连接不能空闲1min 每隔30s发数据包给服务器 保证连接不中断
		数据量小,实时传输要求不高
		消息推送
		精准营销(特定用户推特定消息)
		
		手机端 timer  10min	发送数据包(udp)
		数据包包含用户信息 id...  对比  推送
		
		

		5、业务操作

//划分类结构  测试用例的使用与debug

		以用户登录为例
		public class UserEngineImpl extends BaseEngine implements UserEngine{
			
			public Message login(User user) {
			 Message result = getResult(xml);  //BaseEngine中的方法
			}
		// .....
		}
		
		public class User {
			private String username;
			private String password;
		}
		public abstract class BaseEngine {
	 //并没有抽象方法
			public Message getResult(String xml) {
	//操作......
			}
		}
		
		
 /**
 * 用户相关的业务操作的接口
  */
 public interface UserEngine {
 	
 	Message login(User user);
 	
	Message getBalance(User user);//获取用户余额
	
	Message bet(User user);//用户投注
}



6、业务隔离
//工厂模式 反射与配置

BeanFactory
bean.properties文件放在src文件夹下
UserEngine=com.ithm.lotteryhm28.engine.impl.UserEngineImpl
CommonInfoEngine=com.ithm.lotteryhm28.engine.impl.CommonInfoEngineImpl

很现实的一个功能 :
降低耦合度
Aty需要改动 创建Aty1  需要改动好多引用Aty的文件  工厂类只需修改配置文件即可

实现类的调用 UserLoginImp user = BeanFactory.getImpl(UserLoginImp.class);	//要改的话只需要改这一行  还能扩展	

public class BeanFactory {
	// 依据配置文件加载实例
	
	private static Properties properties;
	static{
		properties=new Properties();
		// bean.properties必须在src的跟目录下
		try {
			properties.load(BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * 加载需要的实现类
	 * @param clazz
	 * @return
	 */
	public static<T> T getImpl(Class<T> clazz)
	{
		String key=clazz.getSimpleName();//clazz.getName()  关键
		String className = properties.getProperty(key);
		try {
			return (T) Class.forName(className).newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

7、界面的划分

页面过多 需要界面统一  划分:头 中部 底部   标题 底部导航

il_main.xml  头 中部 底布局

头 底部的布局是include进的

ps:  底部布局是相对布局中加上 
android:layout_alignParentBottom="true"

布局都是用相对布局写的 几个重要属性:
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dip"
android:layout_toLeftOf="@id/ii_title_regist"



include  include Realilayout above bottom
一个业务有几十个页面,不应用activity 系统不提供退出应用的方法
应用activityManager管理

黑屏  随机闪出之前的activity

命名规则
做成jar包调用,写id配置文件,如果有重名很复杂
把jar 配置文件 资源文件 让应用管理jar  自定义控件 代码维护 isLibrary的用处

8.头 底 布局管理
底部导航一样的 几中布局的显示和隐藏管理
布局文件写几种title的相对布局
在TitleManager中  showLoginTitle showCommonTitle  showUnloginTitle
需要那个显示哪个 隐藏另外的
//确定唯一activity的id  在TitleManager中  
public void init(Activity activity) {
	commonContainer = (RelativeLayout) activity.findViewById(R.id.ii_common_container);
	setListener();
}
	//MainActivity中调用
private void init() {
		TitleManager manager = TitleManager.getInstance();  //单例
		manager.init(this);
		manager.showUnLoginTitle();
	}



	三)
1、界面切换的核心方法
ViewGroup.addView(view);//覆盖  切换动画
removeView(view);//清理


2、界面切换的简单动画
位移动画
3、复杂的切换动画
AnimationController.java

4、切换界面通用处理
BaseUI  构造里有Context


5、不使用Handler，任意点击按钮切换
MidManager
view.getContext();

6、频繁点击——目标界面的创建
7、频繁点击——界面
      //频繁点击的处理步骤
a.每次点击就会创建new界面
//传递.class对象  
MiddleManager.getInstance().changeUI(UserLogin.class);// changeUI需要修改，不能传递对象，但是明确目标
//泛型 
public void changeUI(Class<? extends BaseUI> targetClazz)
b.每次切换没有判断当前正在展示和需要切换的目标是否是同一界面

//创建过考虑重用
//创建过的界面用集合存起来
// 利用手机内存空间，换应用应用的运行速度
	private static Map<String, BaseUI> VIEWCACHE;// :唯一的标示BaseUI的子类


	public void changeUI(Class<? extends BaseUI> targetClazz) {
		// 判断：当前正在展示的界面和切换目标界面是否相同  1
		if (currentUI != null && currentUI.getClass() == targetClazz) {
			return;
		}

		BaseUI targetUI = null;
		// 一旦创建过，重用  2
		// 判断是否创建了——曾经创建过的界面需要存储 3
		String key = targetClazz.getSimpleName();
		if (VIEWCACHE.containsKey(key)) {
			// 创建了，重用
			targetUI = VIEWCACHE.get(key);
		} else {
			// 否则，创建   4  用构造器创建新对象
			try {
				Constructor<? extends BaseUI> constructor = targetClazz.getConstructor(Context.class);
				targetUI = constructor.newInstance(getContext());
				VIEWCACHE.put(key, targetUI);
			} catch (Exception e) {
				throw new RuntimeException("constructor new instance error");
			}
		}

		c.目标界面每次创建新的控件   浪费资源  比如只是settext不同
	//在构造中创建view	BaseUI的构造	
		public BaseUI(Context context) {
			this.context = context;
			init();
			setListener();
		}	

//SecondUI中对init()的实现

/**
	 * 初始化：调用一次
	 */
public void init() {
		// 简单界面：
	textView = new TextView(context);

	LayoutParams layoutParams = textView.getLayoutParams();
	layoutParams = new LayoutParams(LayoutParams.FILL_PARENT,
		LayoutParams.FILL_PARENT);
	textView.setLayoutParams(layoutParams);

	textView.setBackgroundColor(Color.RED);
	textView.setText("这是第二个界面");
}
	//返回的是唯一的view
public View getChild() {
	return textView;
}



8、返回键操作思路

返回键--->任务栈  
LinkedList 记录 获取栈顶 删除栈顶  有序集合
在MainActivity中添加
9、返回键操作
MiddleManager中判断返回操作 boolean
	private LinkedList<String> HISTORY = new LinkedList<String>();// 用户操作的历史记录

/**
	 * 返回键处理
	 * 
	 * @return
	 */
public boolean goBack() {
		// 记录一下用户操作历史
		// 频繁操作栈顶（添加）——在界面切换成功
		// 获取栈顶
		// 删除了栈顶
		// 有序集合
	if (HISTORY.size() > 0) {
			// 当用户误操作返回键（不退出应用）
		if (HISTORY.size() == 1) {
			return false;
		}

			// Throws:NoSuchElementException - if this LinkedList is empty.
		HISTORY.removeFirst();

		if (HISTORY.size() > 0) {
				// Throws:NoSuchElementException - if this LinkedList is empty.
			String key = HISTORY.getFirst();

				BaseUI targetUI = VIEWCACHE.get(key);//曾经存储的baseUI
				if (targetUI != null) {
					currentUI.onPause();
					middle.removeAllViews();
					middle.addView(targetUI.getChild());
					targetUI.onResume();
					currentUI = targetUI;

					changeTitleAndBottom();
				}else{
					// 处理方式一：创建一个新的目标界面：存在问题——如果有其他的界面传递给被删除的界面
					// 处理方式二：寻找一个不需要其他界面传递数据——跳转到首页
					changeUI(Hall.class);
					PromptManager.showToast(getContext(), "应用在低内存下运行");
				}

				return true;

			}
		}

		return false;
	}



	10、容器联动——比对依据
//中间容器的id  
	BaseUI 中 让每个子类都有自己的id
	public abstract int getID();


	11、降低三个容器的耦合度思路
// 降低三个容器的耦合度思路  通过唯一标示传递 仅仅通知变动
		// 当中间容器变动的时候，中间容器“通知”其他的容器，你们该变动了，唯一的标示传递，其他容器依据唯一标示进行容器内容的切换
		// 通知：
		// 广播：  场景 多个应用
		// 为中间容器的变动增加了监听——   观察者设计模式
	观察者设计模式

	被观察的对象 --->中间容器

	观察者 ----> 标题 底部导航  (左右菜单)

	被观察的对象   {

//这俩个Observable默认有实现
		List<观察者> lists;
		boolean isChange = false;



		setChange(){
			isChange = true;
		};
		notify{
			for(观察者 item : lists){
				item.update();
			}
		}	
	}


	12、降低三个容器的耦合度代码实现



	观察者应用的具体实现步骤:
// ①将中间容器变成被观察的对象
	MidManager extends Observable{
	// ④一旦中间容器变动，修改boolean，然后通知所有的观察者.update()
		setChanged();
		notifyObservers(currentUI.getID());
	}

// ②标题和底部导航变成观察者
	public class BottomManager implements Observer 	
	public class TitleManager implements Observer {
		@Override
		public void update(Observable observable, Object data) {

		//具体是实现观察结果
			if (data != null && StringUtils.isNumeric(data.toString())) {
				int id = Integer.parseInt(data.toString());
				switch (id) {
					case ConstantValue.VIEW_FIRST:
					showUnLoginTitle();
					break;
					case ConstantValue.VIEW_SECOND:
					case ConstantValue.VIEW_SSQ:
					case ConstantValue.VIEW_SHOPPING:
					case ConstantValue.VIEW_LOGIN:
					case ConstantValue.VIEW_PREBET:
					showCommonTitle();
					break;

					case ConstantValue.VIEW_HALL:
					if (GlobalParams.isLogin) {
						showLoginTitle();
						String info = "用户名：" + GlobalParams.USERNAME + "\r\n" + "余额:" + GlobalParams.MONEY;
						userInfo.setText(info);
					} else {
						showUnLoginTitle();
					}
					break;
				}
			}

		}
	}
	
	
// ③建立观察者和被观察者之间的关系（标题和底部导航添加到观察者的容器里面）
	MainActivity{
		init(){
			MiddleManager.getInstance().addObserver(TitleManager.getInstance());
			MiddleManager.getInstance().addObserver(BottomManager.getInstrance());
		}

	}	
	13、日志和显示管理
	信息提示管理类 PromptManager 包括:
	a.当判断当前手机没有网络时使用
	b.退出系统
	c.显示错误提示框

	
	14、加载第一个复杂的界面——购彩大厅

	会出现界面没有填满的bug
	//inflate源码  ViewGroup root  =  null  
	原因:
	ViewGroup.LayoutParmas parmas = null;
	if(root != null){
		parmas = root.generateLayoutParmas(attrs);
	}
	ListView中自动自配了params
	解决:
	showInMiddle = (LinearLayout) View.inflate(context, R.layout.il_hall, null);
	if (showInMiddle.getLayoutParmas() == null) {
		LinearLayout.layoutParams parmas = new LinearLayout.layoutParams(Match,Match);
		showInMiddle.setLayoutParams(parmas);
	}


	15、通用三步代码抽取


	四
	1、获取当前服务器信息

	新建线程访问网路 应对无网络状况
	覆盖thread中的run方法  
	synchronized void start (){}
	super.start();
	
	private class MyThread extends Thread {

		@Override
        public void run() { // TODO 访问网络获取数据 super.run(); }

        @Override
        public synchronized void start () {
        	if
        		(NetUtil.checkNet(context)) {
        			super.start();
        		} else {
        			PromptManager.showNoNetWork(context);
        		}
        	}
        }

        异步任务 AsynTask<URL , Integer,Long> 原理是用线程池
        线程池  ThreadPoolExcutor.execute(Runnable runnable);
//省去判断网络的有无
        public final AsyncTask<Integer, Void, Message> executeProxy( Integer... params){
        	if (NetUtil.checkNet(context)) { 
        		return super.execute(params);
        	} else {
        		PromptManager.showNoNetWork(context); } return null; 
        	}
        } 

        父类BaseUI 中   
/**
	 * 访问网络的工具
	 * 
	 * @author Administrator
	 * 
	 * @param <Params>
	 */
protected abstract class MyHttpTask<Params> extends
AsyncTask<Params, Void, Message> {
		/**
		 * 类似与Thread.start方法 由于final修饰，无法Override，方法重命名 省略掉网络判断
		 * 
		 * @param params
		 * @return
		 */
		public final AsyncTask<Params, Void, Message> executeProxy(
			Params... params) {
			if (NetUtil.checkNet(context)) {
				return super.execute(params);
			} else {
				PromptManager.showNoNetWork(context);
			}
			return null;
		}

	}

	子类Hall中实现
        /**
	 * 获取当前销售期信息(双色球)
	 */
        private void getCurrentIssueInfo() {

		// new MyThread().start();
		// new MyAsyncTask().execute(ConstantValue.SSQ);
        	new MyHttpTask<Integer>() {

        		@Override
        		protected Message doInBackground(Integer... params) {
				// 获取数据——业务的调用
        			CommonInfoEngine engine = BeanFactory.getImpl(CommonInfoEngine.class);
        			return engine.getCurrentIssueInfo(params[0]);
        		}

        		@Override
        		protected void onPostExecute(Message result) {
				// 更新界面
        			if (result != null) {
        				Oelement oelement = result.getBody().getOelement();

        				if (ConstantValue.SUCCESS.equals(oelement.getErrorcode())) {
        					changeNotice(result.getBody().getElements().get(0));
        				} else {
        					PromptManager.showToast(context, oelement.getErrormsg());
        				}
        			} else {
					// 可能：网络不通、权限、服务器出错、非法数据……
					// 如何提示用户
        				PromptManager.showToast(context, "服务器忙，请稍后重试……");
        			}

        			super.onPostExecute(result);
        		}
		}.executeProxy(ConstantValue.SSQ);  //url?

	}
	2、倒计时处理
        // 方式一：
		// adapter.notifyDataSetChanged();// 所有的item更新

		// 方式二：更新需要更新内容（没有必要刷新所有的信息）
		// 获取到需要更新控件的应用
		// TextView view = (TextView) needUpdate.get(0);
		// view.setText(text);

		// 方式三：不想维护needUpdate，如何获取需要更新的控件的引用
		// 将所有的item添加到ListView ，是不是有方式可以获取到ListView的孩子
		// categoryList.findViewById(R.id.ii_hall_lottery_summary);

		// tag The tag to search for, using "tag.equals(getTag())".
	adapter 中 
		holder.text.setTag(position);//设置唯一标示
		TextView view = (TextView) categoryList.findViewWithTag(0);// tag :唯一  关键
		if (view != null) {
			view.setText(text);
		}
		3、ViewPager 原生选项卡 系统原生选项卡
		ViewPagerTraip原生标题卡

		复写PagerAdapter中的 CharSequence getPagerTitle(int position);
return tiltes[position];
		选项卡
		<android.support.v4.view.PagerTabStrip
		android:id = "@+id/pargerTab"
		android:layout_width = "wrap_content"
		android:layout_height = "wrap_content"
		android.support.v4.view.PagerTabStrip/>


pagerTabStrip.setDrawFullUnderline(false);
pagerTabStrip.setTabIndicatorColor(Color.RED);

		实现PagerAdapter中未实现的方法

		4、购彩大厅引入ViewPager

中心任务是计算指示图片在title中间 距离左边的偏移量:



		主要处理指示线的动画  图片资源以矩阵显示

任意修改图片位置 用 矩阵 scaleType="matrix"

		注意: 宽度覆盖 图片矩阵绘图
		android:layout_width="fill_parent"
		android:scaleType="matrix"
		<ImageView
		android:id="@+id/ii_category_selector"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:scaleType="matrix"
		android:src="@drawable/id_category_selector" 
		/>
		屏幕宽度 在Activity中获得,做成全局变量 
		GlobalParams.WIN_WIDTH
/**
	 * 屏幕的宽度
	 */
public static int WIN_WIDTH=0;

要得到指示图片的宽度在:
		// 小图片的宽度
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
	R.drawable.id_category_selector);
		//边距
int offset = (GlobalParams.WIN_WIDTH / 3 - bitmap.getWidth()) / 2;

		// 设置图片初始位置——向右偏移
Matrix matrix = new Matrix();
matrix.postTranslate(offset, 0);
indicateIV.setImageMatrix(matrix);

滑动时的移动
// 记录ViewPger上一个界面的position信息
private int lastPosition = 0;

pager.addOnPageChangeListener(new OnPageChangeListener() {
	@Override
	public void onPageSelected(int position) {
                // position:0

                // fromXDelta toXDelta:相对于图片初始位置需要增加的量
//位置的确定


		TranslateAnimation animation = new TranslateAnimation(
			lastPosition * GlobalParams.WIN_WIDTH / 3,
			position * GlobalParams.WIN_WIDTH / 3,
			0, 0);
		animation.setDuration(300);
                animation.setFillAfter(true);// 移动完后停留到终点

                underLine.startAnimation(animation);
                lastPosition = position;  //lastPosition要变更
            }
        }

        5、标题内容确定
        数据传递用Bundle

// ①标题
	// 判断购彩大厅是否获取到期次信息
	// 如果获取到：拼装标题
	// 否则默认的标题展示

        6、选号容器填充

        球用textView加个红球灰球的背景
        TextView ball = new TextView(context);
        //号码两位格式
        DecimalFormat decimalFormat = new DecimalFormat("00");
        ball.setText(decimalFormat.format(position + 1));
        ball.setTextSize(16);
		// 居中
        ball.setGravity(Gravity.CENTER);


        7、选号处理（防淘宝） 点击动画
        放大镜效果很屌  再看

        点击左右晃动动画
      //  指定移动的  轨迹 
     //   android:interpolator="@android:anim/cycle_interpolator"
      //  cycle_interpolator:按照正弦曲线
        Animation文档里有各种动画轨迹
        <?xml version="1.0" encoding="utf-8"?>
        <translate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="120"
        android:fromXDelta="0"
        android:interpolator="@android:anim/cycle_interpolator"
        android:repeatCount="3"
        android:toXDelta="5" >

        </translate>

        红球点击滑动放大镜效果
        MyGridView

        两个重要的api
        int position = AbsListView.pointToPosition(x, y);

        if (position == this.INVALID_POSITION) {//无效的位置

			hiddenPop();
			return false;
		}
//相当重要的api    ViewGroup.getChildAt(position);
		TextView child = (TextView) ViewGroup.getChildAt(position);

要解决的问题:
		知道点击的那个位置对应的item 
		是移动的时候pop的位置变化和监听
		接管父布局的touchEvent事件
		view.getParent().requestDisallowInterceptTouchEvent(true);
		滑动时传递的是child
		根据child
		private void updatePop(TextView child) {
		ball.setText(child.getText());
		int yOffset = -(pop.getHeight() + child.getHeight());
		int xOffset = -(pop.getWidth() - child.getWidth()) / 2;
		// width the new width, can be -1 to ignore
		// height the new height, can be -1 to ignore
		//传递child和offst
		pop.update(child, xOffset, yOffset, -1, -1);
	}


	接口监听:
	myGridView.setOnActionUpListener(new OnActionUpListener() {

			@Override
			public void onActionUp(View view, int position) {
				// 判断当前点击的item是否被选中了
				if (!redNums.contains(position + 1)) {
					// 如果没有被选中
					// 背景图片切换到红色
					view.setBackgroundResource(R.drawable.id_redball);
					redNums.add(position + 1);
				} else {
					// 被选中
					// 还原背景图片
					view.setBackgroundResource(R.drawable.id_defalut_ball);
					redNums.remove((Object) (position + 1));
				}

				changeNotice();
			}
		});
移动的时候只与对应的child有关

		// ①手指按下
		// 显示放大的号码
		// ②手指滑动
		// 更新：号码内容+显示位置
		// ③手指抬起
		// 修改手指下面的球的背景


        五
        1、机选红蓝球


        
        2、手机摇晃处理
        		manager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);


listener = new ShakeListener(context) {

			@Override
			public void randomCure() {
				randomSSQ();
			}

		};
		manager.registerListener(listener, manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_FASTEST);
		super.onResume();

        3、修改底部导航显示信息


        /**
	 * 计算一个数的阶乘  递归
	 * 
	 * @param num
	 * @return
	 */
	private long factorial(int num) {
		// num=7 7*6*5...*1

		if (num > 1) {
			return num * factorial(num - 1);
		} else if (num == 1 || num == 0) {
			return 1;
		} else {
			throw new IllegalArgumentException("num >= 0");
		}
	}
        4、清空处理
用接口的方法处理玩法
class PlaySSQ implements PlayGame

/**
 * 玩法相关界面操作
 */
public interface PlayGame {
	/**
	 * 清空
	 */
	void clear();
	/**
	 * 选好了
	 */
	void done();
	
}

        // 清空按钮
		cleanButton.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
				Log.i(TAG, "点击清空按钮");
				// 获取到当前正在展示的界面
				BaseUI current = MiddleManager.getInstance().getCurrentUI();

				  if (current instanceof PlaySSQ) {
					  ((PlaySSQ) current).clear(); 
				  }
				
//重要 用接口 多态
				if (current instanceof PlayGame) {
					((PlayGame) current).clear();//多态
				}

			}
		});
		// 选好按钮
		addButton.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
				Log.i(TAG, "点击选好按钮");
				BaseUI current = MiddleManager.getInstance().getCurrentUI();
				if (current instanceof PlayGame) {
					((PlayGame) current).done();
				}

			}
		});



        
        5、选好了处理（一）
        6、选好了处理（二）

        进入购物车和准备购物车的数据封装 Ticket 和 ShoppingCart


        7、购物车工作清单
        8、购物车ListView填充
        9、选号处理
        <string name="is_play_bottom_title">   共 NUM  注   MONEY  元   </string>
    <string name="is_shopping_list_title" formatted="false">N% I%期</string>
    <string name="is_shopping_list_notice" 
    formatted="false">
    <![CDATA[注数：&#160;&#160;<font color="#ff0000">NUM</font>&#160;&#160;注&#160;&#160;金额：&#160;&#160;<font color="#ff0000">MONEY</font>元]]>
    </string>

/**
	 * 更换html文本
	 */
	private void changeNotice() {
		Integer lotterynumber = ShoppingCart.getInstance().getLotterynumber();
		Integer lotteryvalue = ShoppingCart.getInstance().getLotteryvalue();

		String noticeInfo = context.getResources().getString(R.string.is_shopping_list_notice);
		noticeInfo = StringUtils.replaceEach(noticeInfo, new String[] { "NUM", "MONEY" }, new String[] { lotterynumber.toString(), lotteryvalue.toString() });

		// Html.fromHtml(notice):将notice里面的内容转换
		notice.setText(Html.fromHtml(noticeInfo));
	}

        10、购物车购买操作
        11、用户登录
        12、追期和倍投的设置界面——工作清单
        13、追期和倍投的设置处理

        增加和减少倍数
     case R.id.ii_add_appnumbers:
			// 增加倍数
			boolean result = ShoppingCart.getInstance().addAppnumbers(true);
			if (result) {
				changeNotice();
			}
			break;
		case R.id.ii_sub_appnumbers:
			// 减少倍数
			if (ShoppingCart.getInstance().addAppnumbers(false)) {
				changeNotice();
			}
			break;

	/**
	 * 操作倍数
	 */
	public boolean addAppnumbers(boolean isAdd) {
		if (isAdd) {
			appnumbers++;
			if (appnumbers > 99) {
				appnumbers--;
				return false;
			}

			if (getLotteryvalue() > GlobalParams.MONEY) {
				appnumbers--;
				return false;
			}
		} else {
			appnumbers--;
			if (appnumbers == 0) {
				appnumbers++;
				return false;
			}
		}
		return true;
	}


        六
        1、构建高速缓存（一）

        // 利用手机内存空间，换应用应用的运行速度
	private static Map<String, BaseUI> VIEWCACHE;// K
	// :唯一的标示BaseUI的子类

	static {
		// 16M，如果不足<16M(模拟器)
		// 32M，真机
		if (MemoryManager.hasAcailMemory()) {
			VIEWCACHE = new HashMap<String, BaseUI>();
		} else {
			VIEWCACHE = new SoftMap<String, BaseUI>();
		}
	}
内存管理类 MemoryManager



        2、构建高速缓存（二）
        3、LruCache简答介绍
        4、数据库操作——问题清单
        5、表名的获取
        6、将实体数据导入到数据库
        7、获取到主键
        8、将数据库信息导入到实体
        9、实例的创建
        10、数据库操作的步骤
        11、数据库版本的维护




